Skip to content

Auto-find available port and version-check running instances#13

Merged
lukekim merged 3 commits into
trunkfrom
worktree-stateless-chasing-galaxy
Apr 2, 2026
Merged

Auto-find available port and version-check running instances#13
lukekim merged 3 commits into
trunkfrom
worktree-stateless-chasing-galaxy

Conversation

@lukekim
Copy link
Copy Markdown
Contributor

@lukekim lukekim commented Apr 2, 2026

Summary

  • Auto-increment port: TCP listener binds before SMB setup; if the requested port is busy, tries the next port (up to 100) instead of panicking.
  • Versioned Server header: Server: spiceio/X.Y.Z replaces bare Server: spiceio, enabling the setup action to detect stale instances.
  • Setup action version check: Compares the running instance's version against the installed binary. Mismatched or pre-versioned instances are killed and replaced. Bare Server: spiceio (no version) is treated as outdated.
  • Endpoint discovery: Readiness loop parses the actual bound address from SPICEIO_LOG_FILE so the action's endpoint output is correct even when the port shifted.

Test plan

  • Verify cargo check / cargo build passes
  • Run with port 8333 free — binds normally
  • Run with port 8333 occupied — auto-increments to 8334+
  • Confirm Server response header is spiceio/0.4.0
  • Run setup action with no prior instance — starts and reports correct endpoint
  • Run setup action with same-version instance — skips start
  • Run setup action with old-version instance — replaces it

- Bind TCP listener before SMB setup; auto-increment port on AddrInUse
  (up to 100 tries) so the proxy never fails on a busy port.
- Add version to Server header (spiceio/X.Y.Z) so the setup action can
  detect stale instances.
- Setup action now compares running version against installed binary and
  replaces outdated instances. Bare "Server: spiceio" (pre-versioned
  builds) is treated as outdated.
- Readiness loop discovers actual endpoint from SPICEIO_LOG_FILE,
  handling port auto-increment correctly.
Copilot AI review requested due to automatic review settings April 2, 2026 22:20
@lukekim lukekim self-assigned this Apr 2, 2026
@lukekim lukekim enabled auto-merge (squash) April 2, 2026 22:21
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves local/CI ergonomics for running spiceio by making the server more resilient to port conflicts and enabling the GitHub setup action to detect/replace stale running instances based on a versioned Server header.

Changes:

  • Bind the TCP listener before SMB setup and auto-increment the port (up to a limit) when the requested port is already in use.
  • Add a versioned Server: spiceio/X.Y.Z response header to support stale-instance detection.
  • Update the setup composite action to (1) compare running vs installed versions, (2) replace mismatched instances, and (3) discover the actual bound endpoint from the log when the port shifts.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/s3/router.rs Adds a versioned Server header (spiceio/<pkg version>) to responses.
src/main.rs Binds the TCP listener early and auto-increments the port on AddrInUse.
.github/actions/setup/action.yml Adds version checks/replacement logic and discovers the runtime endpoint from logs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/main.rs Outdated
Comment thread .github/actions/setup/action.yml Outdated
Comment thread .github/actions/setup/action.yml
lukekim added 2 commits April 2, 2026 15:30
- Handle checked_add overflow gracefully instead of panicking when
  starting port is near u16::MAX.
- Use mapfile to collect multiple lsof PIDs (IPv4/IPv6) and kill them
  all when replacing an old instance.
- Truncate SPICEIO_LOG_FILE before starting and use tail -1 to pick the
  most recent "listening on" line, preventing stale endpoint discovery.
Copilot AI review requested due to automatic review settings April 2, 2026 22:31
@lukekim lukekim merged commit b9d243c into trunk Apr 2, 2026
6 checks passed
@lukekim lukekim deleted the worktree-stateless-chasing-galaxy branch April 2, 2026 22:34
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

exit 0
# Check if spiceio is already listening on the requested address
WANT_VERSION=$(spiceio --version | awk '{print $2}')
RUNNING_HEADER=$(curl -sf -I "http://${SPICEIO_BIND}/" 2>/dev/null | grep -i '^server:.*spiceio' | tr -d '\r' || true)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curl -sf -I "http://${SPICEIO_BIND}/" uses HEAD on /. The router returns 405 for service-level requests that are not GET (see src/s3/router.rs around the req_bucket.is_empty() match), and -f suppresses headers on 4xx, so RUNNING_HEADER will usually be empty and the version/skip logic won’t trigger. Use a GET request when checking headers (e.g., request / and capture response headers) or remove -f/target a path that supports HEAD (like /$SPICEIO_BUCKET).

Suggested change
RUNNING_HEADER=$(curl -sf -I "http://${SPICEIO_BIND}/" 2>/dev/null | grep -i '^server:.*spiceio' | tr -d '\r' || true)
RUNNING_HEADER=$(curl -sS -D - -o /dev/null "http://${SPICEIO_BIND}/" 2>/dev/null | grep -i '^server:.*spiceio' | tr -d '\r' || true)

Copilot uses AI. Check for mistakes.
exit 1
fi
ENDPOINT=$(grep 'listening on' "$SPICEIO_LOG" 2>/dev/null | grep -o 'http://[^ ]*' | tail -1 || true)
if [[ -n "$ENDPOINT" ]] && curl -sf -I "$ENDPOINT/" 2>/dev/null | grep -qi "server: spiceio"; then
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The readiness probe uses curl -sf -I "$ENDPOINT/" and expects a Server: header. Since / does not support HEAD (router returns 405) and -f hides headers on 4xx, this loop can time out even when the server is up. Consider switching back to a GET-based readiness check and, if you still need headers, capture them from the GET response.

Suggested change
if [[ -n "$ENDPOINT" ]] && curl -sf -I "$ENDPOINT/" 2>/dev/null | grep -qi "server: spiceio"; then
if [[ -n "$ENDPOINT" ]] && curl -sS -D - -o /dev/null "$ENDPOINT/" 2>/dev/null | grep -qi '^server: spiceio'; then

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +103
# Old or mismatched version — kill it so we can replace it
mapfile -t OLD_PIDS < <(lsof -ti "tcp:${SPICEIO_BIND##*:}" -sTCP:LISTEN 2>/dev/null || true)
if (( ${#OLD_PIDS[@]} > 0 )); then
echo "replacing spiceio ${RUNNING_VERSION:-unknown} (PID(s) ${OLD_PIDS[*]}) with $WANT_VERSION"
kill "${OLD_PIDS[@]}" 2>/dev/null || true
for i in $(seq 1 10); do
lsof -ti "tcp:${SPICEIO_BIND##*:}" -sTCP:LISTEN >/dev/null 2>&1 || break
sleep 1
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Port extraction via ${SPICEIO_BIND##*:} breaks for IPv6 binds (e.g. [::1]:8333), and the lsof -ti "tcp:..." form is also inconsistent with the repo’s other scripts/workflows (e.g. .github/workflows/ci.yml:80 uses lsof -i ":$PORT" -sTCP:LISTEN -t). Consider parsing the port in an IPv6-safe way and using the same lsof -i ":$PORT" ... pattern here for portability.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants